package com.google.code.gaeapptest.dao;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Logger;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.code.gaeapptest.domain.TestCategory;
import com.google.code.gaeapptest.domain.TestData;
import com.google.code.gaeapptest.util.ClassScanUtil;

public class DaoImpl implements Dao {
	private static final int MAX_ROWS = 1000; // Only plot latest 1000 points
	private static final int MOVING_AVG = 50;
	private static final Logger LOG = Logger.getLogger(DaoImpl.class.getName());

	public List<TestCategory> getTestCategories() {
		String dir = "war/WEB-INF/classes";
		if (System.getProperty("com.google.appengine.runtime.environment") != null) {
			dir = "WEB-INF/classes";
		}
		List<TestCategory> result = new ArrayList<TestCategory>();
		// Dynamically find the tests on the class path
		ClassScanUtil.findAndStoreTests(result, dir, new File(dir));
		return result;

	}

	@Override
	// http://localhost:8888/PlotData?type=Transaction&test=transactionTest1&callback=jQuery171025856821658089757_1328999087940&_=1328999088073
	// Query the database for all the results of a specific category of test and
	// test name
	public List<TestData> getTestData(String category, String testName) {
		List<TestData> result = new ArrayList<TestData>();
		if (category != null && testName != null) {
			DatastoreService datastore = DatastoreServiceFactory
					.getDatastoreService();
			// Query is descending to get the most recent data
			Query query = new Query("Results")
					.addFilter("category", Query.FilterOperator.EQUAL, category)
					.addFilter("testName", Query.FilterOperator.EQUAL, testName)
					.addSort("time", Query.SortDirection.DESCENDING);
			List<Entity> entities = datastore.prepare(query).asList(
					FetchOptions.Builder.withLimit(MAX_ROWS));
			if (entities != null) {
				for (Entity e : entities) {
					Map<String, Object> m = e.getProperties();
					Object time = m.get("time");
					Object elapsedMs = m.get("elapsedMs");
					long timeLong = Long.parseLong(time.toString());
					double elapsedMsDouble = Double.parseDouble(elapsedMs
							.toString());
					TestData t = new TestData(timeLong, elapsedMsDouble);
					result.add(t);
				}
				// highcharts needs ascending data so sort it here
				Collections.sort(result);
			}
		}
		return result;
	}

	// saves the results of a test to the Results Table
	public void saveResult(Date d, String category, String testName,
			long elapsedMs, String msg) {
		Entity result = new Entity("Results");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss S");
		TimeZone estTime = TimeZone.getTimeZone("EST");
		sdf.setTimeZone(estTime);
		long time = d.getTime();
		result.setProperty("date", sdf.format(d));
		result.setProperty("time", time);
		result.setProperty("category", category);
		result.setProperty("testName", testName);
		result.setProperty("elapsedMs", elapsedMs);
		result.setProperty("result", msg);
		LOG.fine("category:" + category + " test:" + testName + " elapsedMs:"
				+ elapsedMs + " result:" + msg);
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		datastore.put(result);

	}

	@Override
	public void updateMovingAverage(String category, String testName) {
		removeAverage(category, testName);
		List<TestData> tds = getTestData(category, testName);
		double[] a = new double[MOVING_AVG];
		double sum = 0.0;
		int len = tds.size();
		for (int i = 0; i < len; i++) {
			sum -= a[i % MOVING_AVG];
			a[i % MOVING_AVG] = tds.get(i).measure;
			sum += a[i % MOVING_AVG];
			if (i + 1 >= MOVING_AVG) {
//				for (int j = 0; j < MOVING_AVG; j++) {
//					System.out.print(a[j] + " ");
//				}
				Double avg = sum / MOVING_AVG;
				Long avgl = avg.longValue();
//				System.out.println("= " + avgl);
				saveResult(new Date(tds.get(i).date), category, testName
						+ "Average", avgl, "OK");
			}
		}
	}

	public void removeAverage(String category, String testName) {
		final DatastoreService dss = DatastoreServiceFactory
				.getDatastoreService();
		long start = System.currentTimeMillis();
		int deleted = 0;
		while (System.currentTimeMillis() - start < 16384) {
			Query query = new Query("Results")
					.addFilter("category", Query.FilterOperator.EQUAL, category)
					.addFilter("testName", Query.FilterOperator.EQUAL,
							testName + "Average")
					.addSort("time", Query.SortDirection.DESCENDING);
			query.setKeysOnly();
			final ArrayList<Key> keys = new ArrayList<Key>();
			for (final Entity entity : dss.prepare(query).asIterable(
					FetchOptions.Builder.withLimit(128))) {
				keys.add(entity.getKey());
			}
			keys.trimToSize();
			if (keys.size() == 0) {
				break;
			}
			while (System.currentTimeMillis() - start < 16384) {
				try {
					dss.delete(keys);
					deleted += keys.size();
					break;
				} catch (Throwable ignore) {
					continue;
				}
			}
		}
		LOG.info("deleted: " + deleted + " category:" + category + " testName:"
				+ testName);
	}
}
